﻿using System;
using GeoAPI.Geometries;
using NetTopologySuite.Algorithm;
using NetTopologySuite.Geometries;

namespace NetTopologySuite.Simplify
{
	public class TaggedLineStringSimplifier
	{
		public TaggedLineStringSimplifier(LineSegmentIndex inputIndex, LineSegmentIndex outputIndex)
		{
			this._inputIndex = inputIndex;
			this._outputIndex = outputIndex;
		}

		public double DistanceTolerance
		{
			get
			{
				return this._distanceTolerance;
			}
			set
			{
				this._distanceTolerance = value;
			}
		}

		public void Simplify(TaggedLineString line)
		{
			this._line = line;
			this._linePts = line.ParentCoordinates;
			this.SimplifySection(0, this._linePts.Length - 1, 0);
		}

		private void SimplifySection(int i, int j, int depth)
		{
			depth++;
			int[] array = new int[2];
			if (i + 1 == j)
			{
				LineSegment segment = this._line.GetSegment(i);
				this._line.AddToResult(segment);
				return;
			}
			bool flag = true;
			if (this._line.ResultSize < this._line.MinimumSize && depth + 1 < this._line.MinimumSize)
			{
				flag = false;
			}
			double[] array2 = new double[1];
			int num = this.FindFurthestPoint(this._linePts, i, j, array2);
			if (array2[0] > this._distanceTolerance)
			{
				flag = false;
			}
			LineSegment lineSegment = new LineSegment();
			lineSegment.P0 = this._linePts[i];
			lineSegment.P1 = this._linePts[j];
			array[0] = i;
			array[1] = j;
			if (this.HasBadIntersection(this._line, array, lineSegment))
			{
				flag = false;
			}
			if (flag)
			{
				LineSegment seg = this.Flatten(i, j);
				this._line.AddToResult(seg);
				return;
			}
			this.SimplifySection(i, num, depth);
			this.SimplifySection(num, j, depth);
		}

		private int FindFurthestPoint(Coordinate[] pts, int i, int j, double[] maxDistance)
		{
			LineSegment lineSegment = new LineSegment();
			lineSegment.P0 = pts[i];
			lineSegment.P1 = pts[j];
			double num = -1.0;
			int result = i;
			for (int k = i + 1; k < j; k++)
			{
				Coordinate p = pts[k];
				double num2 = lineSegment.Distance(p);
				if (num2 > num)
				{
					num = num2;
					result = k;
				}
			}
			maxDistance[0] = num;
			return result;
		}

		private LineSegment Flatten(int start, int end)
		{
			Coordinate p = this._linePts[start];
			Coordinate p2 = this._linePts[end];
			LineSegment lineSegment = new LineSegment(p, p2);
			this.Remove(this._line, start, end);
			this._outputIndex.Add(lineSegment);
			return lineSegment;
		}

		private bool HasBadIntersection(TaggedLineString parentLine, int[] sectionIndex, LineSegment candidateSeg)
		{
			return this.HasBadOutputIntersection(candidateSeg) || this.HasBadInputIntersection(parentLine, sectionIndex, candidateSeg);
		}

		private bool HasBadOutputIntersection(LineSegment candidateSeg)
		{
			foreach (LineSegment seg in this._outputIndex.Query(candidateSeg))
			{
				if (this.HasInteriorIntersection(seg, candidateSeg))
				{
					return true;
				}
			}
			return false;
		}

		private bool HasBadInputIntersection(TaggedLineString parentLine, int[] sectionIndex, LineSegment candidateSeg)
		{
			foreach (LineSegment lineSegment in this._inputIndex.Query(candidateSeg))
			{
				TaggedLineSegment taggedLineSegment = (TaggedLineSegment)lineSegment;
				if (this.HasInteriorIntersection(taggedLineSegment, candidateSeg) && !TaggedLineStringSimplifier.IsInLineSection(parentLine, sectionIndex, taggedLineSegment))
				{
					return true;
				}
			}
			return false;
		}

		private static bool IsInLineSection(TaggedLineString line, int[] sectionIndex, TaggedLineSegment seg)
		{
			if (seg.Parent != line.Parent)
			{
				return false;
			}
			int index = seg.Index;
			return index >= sectionIndex[0] && index < sectionIndex[1];
		}

		private bool HasInteriorIntersection(LineSegment seg0, LineSegment seg1)
		{
			this._li.ComputeIntersection(seg0.P0, seg0.P1, seg1.P0, seg1.P1);
			return this._li.IsInteriorIntersection();
		}

		private void Remove(TaggedLineString line, int start, int end)
		{
			for (int i = start; i < end; i++)
			{
				TaggedLineSegment segment = line.GetSegment(i);
				this._inputIndex.Remove(segment);
			}
		}

		private readonly LineIntersector _li = new RobustLineIntersector();

		private readonly LineSegmentIndex _inputIndex = new LineSegmentIndex();

		private readonly LineSegmentIndex _outputIndex = new LineSegmentIndex();

		private TaggedLineString _line;

		private Coordinate[] _linePts;

		private double _distanceTolerance;
	}
}
